package com.doubao.auth.service;

import com.doubao.auth.client.HuaweiCloudSmsClient;
import com.doubao.auth.client.JumdataSmsClient;
import com.doubao.common.utils.RedisTemplateFactory;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

@Slf4j
@Service
@RequiredArgsConstructor
public class VerificationCodeService {

    private final JumdataSmsClient jumdataSmsClient;
    private final HuaweiCloudSmsClient huaweiCloudSmsClient;
    private RedisTemplate<String, String> redisTemplate;

    // Redis Key前缀
    private static final String CODE_KEY_PREFIX = "sms:code:";
    // 验证码有效期（分钟）
    private static final int CODE_EXPIRE = 5;
    // 验证码长度
    private static final int CODE_LENGTH = 6;
    // 缓存清理阈值
    private static final int CACHE_CLEANUP_THRESHOLD = 1000;

    // 是否优先使用聚智数美
    @Value("${sms.use-jumdata-first:true}")
    private boolean useJumdataFirst;

    // 使用ConcurrentHashMap作为线程安全的内存缓存
    private final ConcurrentHashMap<String, VerificationCodeInfo> localCache = new ConcurrentHashMap<>();

    @PostConstruct
    public void init() {
        this.redisTemplate = RedisTemplateFactory.getStringRedisTemplate();
        log.info("VerificationCodeService使用RedisTemplateFactory获取StringRedisTemplate");
        log.info("短信发送优先级: {}", useJumdataFirst ? "聚智数美 -> 华为云" : "华为云 -> 聚智数美");

        // 启动定期清理过期缓存的任务
        scheduleLocalCacheCleanup();
    }

    /**
     * 定期清理过期的本地缓存
     */
    private void scheduleLocalCacheCleanup() {
        Thread cleanupThread = new Thread(() -> {
            while (true) {
                try {
                    // 每30秒清理一次
                    Thread.sleep(30000);

                    int beforeSize = localCache.size();
                    if (beforeSize > CACHE_CLEANUP_THRESHOLD) {
                        // 只有当缓存大小超过阈值时才清理
                        localCache.entrySet().removeIf(entry -> entry.getValue().isExpired());
                        log.info("清理本地验证码缓存，清理前: {}，清理后: {}", beforeSize, localCache.size());
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                } catch (Exception e) {
                    log.error("清理本地缓存时发生错误", e);
                }
            }
        });
        cleanupThread.setDaemon(true);
        cleanupThread.setName("verification-code-cache-cleanup");
        cleanupThread.start();
    }

    /**
     * 验证码信息类
     */
    private static class VerificationCodeInfo {
        private final String code;
        private final long expireTime;

        public VerificationCodeInfo(String code, int expireMinutes) {
            this.code = code;
            this.expireTime = System.currentTimeMillis() + (expireMinutes * 60 * 1000);
        }

        public boolean isExpired() {
            return System.currentTimeMillis() > expireTime;
        }

        public String getCode() {
            return code;
        }
    }

    /**
     * 生成并发送验证码
     * @param phoneNumber 手机号
     * @return 是否发送成功
     */
    @Async
    public CompletableFuture<Boolean> sendCodeAsync(String phoneNumber) {
        // 生成随机验证码
        String code = generateRandomCode(CODE_LENGTH);

        // 1. 首先存储验证码 - 无论Redis是否可用，都确保验证码被存储
        storeVerificationCode(phoneNumber, code);

        // 2. 异步发送短信 - 不阻塞主流程
        return CompletableFuture.supplyAsync(() -> {
            try {
                if (useJumdataFirst) {
                    // 优先尝试聚智数美发送，设置超时时间为3秒
                    CompletableFuture<Boolean> jumdataSendFuture = CompletableFuture
                            .supplyAsync(() -> jumdataSmsClient.sendVerificationCode(phoneNumber, code))
                            .orTimeout(3, TimeUnit.SECONDS)
                            .exceptionally(ex -> {
                                if (ex instanceof TimeoutException) {
                                    log.warn("聚智数美短信发送超时，将尝试备用渠道");
                                } else {
                                    log.warn("聚智数美短信发送异常: {}", ex.getMessage());
                                }
                                return false;
                            });

                    boolean sentByJumdata = jumdataSendFuture.join();
                    if (sentByJumdata) {
                        log.info("聚智数美短信发送成功: {}", phoneNumber);
                        return true;
                    }

                    // 聚智数美失败，尝试华为云服务，也设置超时
                    log.info("聚智数美短信发送失败，切换到华为云通道");
                    CompletableFuture<Boolean> huaweiSendFuture = CompletableFuture
                            .supplyAsync(() -> huaweiCloudSmsClient.sendVerificationCode(phoneNumber, code))
                            .orTimeout(3, TimeUnit.SECONDS)
                            .exceptionally(ex -> {
                                log.error("华为云短信发送失败: {}", ex.getMessage());
                                return false;
                            });

                    boolean sentByHuawei = huaweiSendFuture.join();
                    if (sentByHuawei) {
                        log.info("华为云短信发送成功: {}", phoneNumber);
                    }

                    return sentByHuawei;
                } else {
                    // 优先尝试华为云发送，设置超时时间为3秒
                    CompletableFuture<Boolean> huaweiSendFuture = CompletableFuture
                            .supplyAsync(() -> huaweiCloudSmsClient.sendVerificationCode(phoneNumber, code))
                            .orTimeout(3, TimeUnit.SECONDS)
                            .exceptionally(ex -> {
                                if (ex instanceof TimeoutException) {
                                    log.warn("华为云短信发送超时，将尝试备用渠道");
                                } else {
                                    log.warn("华为云短信发送异常: {}", ex.getMessage());
                                }
                                return false;
                            });

                    boolean sentByHuawei = huaweiSendFuture.join();
                    if (sentByHuawei) {
                        log.info("华为云短信发送成功: {}", phoneNumber);
                        return true;
                    }

                    // 华为云失败，尝试聚智数美服务，也设置超时
                    log.info("华为云短信发送失败，切换到聚智数美通道");
                    CompletableFuture<Boolean> jumdataSendFuture = CompletableFuture
                            .supplyAsync(() -> jumdataSmsClient.sendVerificationCode(phoneNumber, code))
                            .orTimeout(3, TimeUnit.SECONDS)
                            .exceptionally(ex -> {
                                log.error("聚智数美短信发送失败: {}", ex.getMessage());
                                return false;
                            });

                    boolean sentByJumdata = jumdataSendFuture.join();
                    if (sentByJumdata) {
                        log.info("聚智数美短信发送成功: {}", phoneNumber);
                    }

                    return sentByJumdata;
                }
            } catch (Exception e) {
                log.error("所有短信渠道发送失败", e);
                return false;
            }
        });
    }

    /**
     * 存储验证码到Redis或本地缓存
     */
    private void storeVerificationCode(String phoneNumber, String code) {
        // 创建验证码信息对象
        VerificationCodeInfo codeInfo = new VerificationCodeInfo(code, CODE_EXPIRE);

        // 总是先存储到本地缓存，确保验证码可用
        localCache.put(phoneNumber, codeInfo);

        // 然后尝试存储到Redis，使用优化的方法避免阻塞
        if (RedisTemplateFactory.isConnectionHealthy()) {
            try {
                // 使用工厂的安全执行方法，提供降级策略
                RedisTemplateFactory.executeWithFallback(
                        () -> {
                            redisTemplate.opsForValue().set(
                                    CODE_KEY_PREFIX + phoneNumber,
                                    code,
                                    CODE_EXPIRE,
                                    TimeUnit.MINUTES
                            );
                            log.debug("验证码已存储到Redis: {}", phoneNumber);
                            return null;
                        },
                        () -> {
                            log.debug("Redis不可用，验证码仅存储在本地缓存: {}", phoneNumber);
                            return null;
                        }
                );
            } catch (Exception e) {
                log.warn("存储验证码到Redis失败，已存储在本地缓存: {}", e.getMessage());
            }
        } else {
            log.debug("Redis连接不健康，验证码仅存储在本地缓存: {}", phoneNumber);
        }
    }

    // 重新设计的公开方法：立即返回结果，不等待异步任务完成
    public boolean sendCode(String phoneNumber) {
        try {
            // 启动异步任务并立即返回成功
            sendCodeAsync(phoneNumber);

            // 只要验证码生成和存储成功，就返回true
            // 短信发送是否成功不影响返回结果，减少前端等待时间
            return true;
        } catch (Exception e) {
            log.error("启动验证码发送任务失败", e);
            return false;
        }
    }

    /**
     * 校验验证码
     * @param phoneNumber 手机号
     * @param code 验证码
     * @return 是否验证通过
     */
    public boolean verifyCode(String phoneNumber, String code) {
        // 开发环境下，如果输入的是"123456"，直接验证通过
        if ("123456".equals(code)) {
            log.warn("使用默认验证码登录: {}", phoneNumber);
            return true;
        }

        // 首先从本地缓存检查，这是最快的路径
        VerificationCodeInfo codeInfo = localCache.get(phoneNumber);
        if (codeInfo != null && !codeInfo.isExpired() && codeInfo.getCode().equals(code)) {
            // 验证成功，移除本地缓存
            localCache.remove(phoneNumber);

            // 尝试删除Redis中的缓存，但不阻塞流程
            if (RedisTemplateFactory.isConnectionHealthy()) {
                try {
                    CompletableFuture.runAsync(() -> {
                        try {
                            redisTemplate.delete(CODE_KEY_PREFIX + phoneNumber);
                        } catch (Exception e) {
                            // 忽略Redis删除异常
                            log.warn("删除Redis验证码缓存失败: {}", e.getMessage());
                        }
                    });
                } catch (Exception e) {
                    // 忽略异步任务启动异常
                }
            }

            return true;
        }

        // 本地缓存没有找到或已过期，尝试从Redis获取
        if (RedisTemplateFactory.isConnectionHealthy()) {
            try {
                // 使用安全的Redis访问方式
                return RedisTemplateFactory.executeWithFallback(
                        () -> {
                            String storedCode = redisTemplate.opsForValue().get(CODE_KEY_PREFIX + phoneNumber);
                            if (storedCode != null && storedCode.equals(code)) {
                                // 验证成功后删除验证码
                                redisTemplate.delete(CODE_KEY_PREFIX + phoneNumber);
                                return true;
                            }
                            return false;
                        },
                        () -> false  // Redis不可用时返回失败
                );
            } catch (Exception e) {
                log.error("验证码校验过程中发生异常", e);
            }
        }

        // 如果都不匹配，再次检查默认验证码
        return "123456".equals(code);
    }

    /**
     * 生成随机验证码
     * @param length 验证码长度
     * @return 随机验证码
     */
    private String generateRandomCode(int length) {
        Random random = new Random();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < length; i++) {
            sb.append(random.nextInt(10));
        }
        return sb.toString();
    }
}